﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Net;
using Innovox.IO.Acquisition;

namespace Innovox.YDeviceLib
{
    /*
     <obj href="http://192.168.192.168:40044/obix/yfm/">
  <int name="dim" val="300" writable="true" href="dim/" /> 
  <bool name="buttonPressed" val="false" href="buttonPressed/" /> 
  <ref name="relay1" href="relay1/" /> 
  <ref name="relay2" href="relay2/" /> 
  </obj>
      
     <obj href="http://192.168.192.168:40044/obix/yfm/relay1/">
  <enum name="switchStatus" range="/obix/enums/switchStatuses/" val="on" href="switchStatus/" /> 
  <bool name="latchActive" val="false" writable="true" href="latchActive/" /> 
  <int name="offDelay" val="60" writable="true" href="offDelay/" /> 
  <bool name="flickWarn" val="false" writable="true" href="flickWarn/" /> 
  <int name="flickOnTime" val="1000" writable="true" href="flickOnTime/" /> 
  <int name="flickOffTime" val="500" writable="true" href="flickOffTime/" /> 
  <int name="flickReps" val="4" writable="true" href="flickReps/" /> 
  <enum name="autoMode" range="/obix/enums/autoModes/" val="manOnAutoOff" writable="true" href="autoMode/" /> 
  </obj>
    */
    public class YFM : YDevice
    {
        #region Constructors

        public YFM(string ip): base(ip)
        {
            InitProperties();
        }

        public YFM(IPAddress ip)
            : base(ip)
        {
            InitProperties();
        }

        #region Helper function

        #endregion

        #endregion Constructors

        private void InitProperties()
        {
            DimOptionOn = false;
            Out24vInstalled = false;
            Relay2Installed = false;
            Relay34Installed = false;
            _optionVerified = false;
            SwitchStatusAr = new string[4];
            _switchStatusBits = new int[4];
            LatchActiveAr = new bool[4];
            SwitchStatusTestPassed = new bool[4];
            RelayTestPassed = new bool[4];
            _dim = -1;
        }

        #region Properties

        bool _optionVerified;
        int[] _switchStatusBits;
        int _inputTestState;
        float _dimTestVoltage;
        //float _24vPreviousVoltage;
        int _24vValidCount = 0;

        DLPIO8G _acqDevice;

        public bool DimOptionOn;
        public bool Out24vInstalled;
        public bool Relay2Installed;
        public bool Relay34Installed;
        public string[] SwitchStatusAr { get; private set; }
        public bool[] LatchActiveAr { get; private set; }
        //public bool ButtonPressed { get; private set; }
        public bool InputState { get; private set; }
        public bool[] RelaysState { get; private set; }
        public float Measured24V;

        public float MeasuredDim;
        /*
        bool _dimEnable;
        public bool DimEnable
        {
            get
            {
                string val = Read("/obix/yfm/dimming/dimEnable/");
                bool result;
                if (bool.TryParse(val, out result) == false)
                    throw (new Exception("Could not read /obix/yfm/dimming/dimEnable/"));

                _dimEnable= result;
                return result;
            }

            set
            {
                //if (_dim != value)
                if (Write("/obix/yfm/dimming/dim/enable/", value.ToString()) == false)
                    throw (new Exception("Could not write /obix/yfm/dimming/dimEnable/"));
                else
                    _dimEnable = value;
            }
        }*/

        int _dim;
        public int Dim
        {
            get
            {
                string val = Read("/obix/yfm/dimming/dim/");
                int result;
                if( int.TryParse(val, out result) == false )
                    throw(new Exception("Could not read /obix/yfm/dimming/dim/"));

                _dim = result;
                return result; 
            }

            set
            {
                //if (_dim != value)
                    if (Write("/obix/yfm/dimming/dim/", value.ToString()) == false)
                        throw (new Exception("Could not write /obix/yfm/dimming/dim/"));
                    else
                        _dim = value;
            }
        }

        int _dimMin;
        public int DimMin
        {
            get
            {
                string val = Read("/obix/yfm/dimming/dimMin/");
                int result;
                if (int.TryParse(val, out result) == false)
                    throw (new Exception("Could not read /obix/yfm/dimming/dimMin/"));

                _dimMin = result;
                return result;
            }

            set
            {
                //if (_dim != value)
                if (Write("/obix/yfm/dimming/dimMin/", value.ToString()) == false)
                    throw (new Exception("Could not write /obix/yfm/dimming/dimMin/"));
                else
                    _dimMin = value;
            }
        }

        string _autoModeRelay1;
        public string AutoModeRelay1
        {
            get
            {
                string val = Read("/obix/yfm/relay1/autoMode/");

                if (val == "" || val == null)
                    throw (new Exception("Could not read /obix/yfm/relay1/autoMode/"));

                _autoModeRelay1 = val;
                return val;
            }

            set
            {
                if (_autoModeRelay1 != value)
                    if (Write("/obix/yfm/relay1/autoMode/", value) == false)
                        throw (new Exception("Could not write /obix/yfm/relay1/autoMode/"));
                    else
                        _autoModeRelay1 = value;
            }
        }

        /*
        bool _buttonPressed;
        public bool ButtonPressed
        {
            get
            {
                string val = Read("/obix/yfm/buttonPressed/");
                bool result;
                if (bool.TryParse(val, out result) == false)
                    throw (new Exception("Could not read /obix/yfm/buttonPressed/"));

                _buttonPressed = result;
                return _buttonPressed;
            }
        }*/

        int _zcdFreq;
        public int ZcdFreq
        {
            get
            {
                string val = Read("/obix/status/zcdFreq/");
                int result;
                if (int.TryParse(val, out result) == false)
                    throw (new Exception("Could not read /obix/status/zcdFreq/"));

                _zcdFreq = result;
                return _zcdFreq;
            }
        }

        public bool[] RelayTestPassed { get; private set; }
        public bool[] SwitchStatusTestPassed { get; private set; }
        //public bool[] SwitchTestPassed { get; private set; }
        //public bool ButtonTestPassed { get; private set; }
        public bool RxTestPassed { get; private set; }
        public bool TxTestPassed { get; private set; }
        public bool Out24vTestPassed { get; private set; }
        public bool DimTestPassed { get; private set; }
        public bool InputTestPassed { get; private set; }
        public bool ZcdTestPassed { get; private set; }

        public bool TestPassed
        {
            get
            {
                return ZcdTestPassed && RelayTestPassed[0] && (RelayTestPassed[1] || Relay2Installed == false) && SwitchStatusTestPassed[0] && (SwitchStatusTestPassed[1] || Relay2Installed == false) && RxTestPassed
                    && TxTestPassed && Out24vTestPassed && (DimTestPassed || DimOptionOn==false) && InputTestPassed;
            }
        }
        #endregion Properties

        #region Hardware Test functions

        private void ReadRelayState(int num)
        {
            int i = num - 1;
            string uri = string.Format("/obix/yfm/relay{0}/switchStatus/", num);
            string val = Value(uri);

            if (val != null)
                SwitchStatusAr[i] = val;
            else
                SwitchStatusAr[i] = "";

            uri = string.Format("/obix/yfm/relay{0}/latchActive/", num);
            val = Value(uri);

            if (val != null)
            {
                bool latch;
                if( bool.TryParse(val, out latch))
                    LatchActiveAr[i] = latch;
            }

        }

        public void ResetTest()
        {
            try
            {
                if (_acqDevice == null)
                    _acqDevice = new DLPIO8G();

                ZcdTestPassed = false;
                _zcdFreq = 0;
                //ButtonTestPassed = false;
                RxTestPassed = false;
                TxTestPassed = false;
                Out24vTestPassed = false;
                DimTestPassed = false;
                _dimTestVoltage = 0;
                //_24vPreviousVoltage = 0;
                _24vValidCount = 0;

                _inputTestState = 0;
                InputTestPassed = false;

                if (_relayTestState == null)
                    _relayTestState = new int[4];

                if (_relayPrevState == null)
                    _relayPrevState = new bool[4];

                if (RelaysState == null)
                    RelaysState = new bool[4];

                for (int i = 0; i < 4; i++)
                {
                    _switchStatusBits[i] = 0;
                    SwitchStatusTestPassed[i] = false;
                    RelayTestPassed[i] = false;
                    _relayTestState[i] = 0;
                    _relayPrevState[i] = false;
                    RelaysState[i] = false;
                }
            }
            catch (Exception e)
            {
                if (_acqDevice != null)
                    _acqDevice.Close();

                _acqDevice = new DLPIO8G();
            }
        }

        public bool RefreshProperties()
        {
            string[] uris;
            string val;

            if (Modem.UpdateInfo() == false)
                return false;

            if (_optionVerified == false)
            {
                uris = new string[] { "/obix/yfm/dimming/dim/", "/obix/yfm/input/",
                                      "/obix/yfm/relay2/switchStatus/", "/obix/yfm/relay3/switchStatus/" };

                BatchOutObjects = Read(uris);

                if (BatchOutObjects == null)
                    return false;

                val = Value(uris[0]);
                DimOptionOn = val != null;
                
                val = Value(uris[1]);
                Out24vInstalled = val != null;

                val = Value(uris[2]);
                Relay2Installed = val != null;

                val = Value(uris[3]);
                Relay34Installed = val != null;

                _optionVerified = true;
            }

            List<string> urisList = new List<string>();

            urisList.Add("/obix/yfm/relay1/switchStatus/");
            urisList.Add("/obix/yfm/relay1/latchActive/");
            urisList.Add("/obix/yfm/relay1/autoMode/");
            //urisList.Add("/obix/yfm/buttonPressed/");
            urisList.Add("/obix/status/zcdFreq/");

            if (Out24vInstalled)
                urisList.Add("/obix/yfm/input/");

            if(DimOptionOn)
                urisList.Add("/obix/yfm/dimming/dim/");

            if (Relay2Installed)
            {
                urisList.Add("/obix/yfm/relay2/switchStatus/");
                urisList.Add("/obix/yfm/relay2/latchActive/");
            }

            if (Relay34Installed)
            {
                urisList.Add("/obix/yfm/relay3/switchStatus/");
                urisList.Add("/obix/yfm/relay3/latchActive/");
                urisList.Add("/obix/yfm/relay4/switchStatus/");
                urisList.Add("/obix/yfm/relay4/latchActive/");
            }

            uris = urisList.ToArray();

            BatchOutObjects = Read(uris);
            
            if (BatchOutObjects == null)
                return false;

            val = Value("/obix/status/zcdFreq/");
            if (val != null)
            {
                int freq;

                if (int.TryParse(val, out freq) == true)
                {
                    _zcdFreq = freq;
                    if (freq >= 57 && freq <= 63)
                        ZcdTestPassed = true;
                }
            }
            ReadRelayState(1);
           /* val = Value("/obix/yfm/buttonPressed/");
            if( val != null )
            {
                bool button;

                if (bool.TryParse(val, out button) == true)
                    ButtonPressed = button;
            }*/

            val = Value("/obix/yfm/relay1/autoMode/");
            if (val != null)
            {
                if(val != "man")
                    AutoModeRelay1 = "man";
            }

            if (Out24vInstalled)
            {
                val = Value("/obix/yfm/input/");

                if(val != null)
                {
                    bool input;

                    if (bool.TryParse(val, out input) == true)
                        InputState = input;
                }
            }

            if (DimOptionOn)
            {
                val = Value("/obix/yfm/dimming/dim/");
            
                if (val != null)
                {
                    int dim;

                    if (int.TryParse(val, out dim) == true)
                        Dim = dim;
                }
            }

            if (Relay2Installed)
                ReadRelayState(2);

            if (Relay34Installed)
            {
                ReadRelayState(3);
                ReadRelayState(4);
            }

            return true;
        }

        public bool Test()
        {
            try
            {
                if (_acqDevice.IsConnected() == false)
                    _acqDevice.Open();

                if (RefreshProperties() == true)
                {
                    TestRelayWithMCS();
                    if (RelayTestPassed[0])
                    {
                        if (Relay2Installed)
                            TestRelay(2);

                        if (Relay34Installed)
                        {
                            TestRelay(3);
                            TestRelay(4);
                        }
                    }
                    if (Out24vInstalled)
                    {
                        Test24vOut();
                        TestInput();
                    }

                    if (DimOptionOn)
                        TestDim();

                    TestBitrate();
                    TestSwitchStatus();
                    return true;
                }
            }
            catch (Exception e)
            {
                if( _acqDevice != null )
                    _acqDevice.Close();
                return false;
            }
            return false;
        }

        const int _inputAcqChannel = 4;

        private void TestInput()
        {
            if (_acqDevice.IsConnected())
            {
                switch(_inputTestState)
                {
                    case 0:
                        _acqDevice.WriteDigOut(_inputAcqChannel, true);
                        break;
                    
                    case 1:
                    case 2:
                        if (InputState == false)
                            _inputTestState = -1;
                        break;

                    case 3:
                        _acqDevice.WriteDigOut(_inputAcqChannel, false);
                        break;

                    case 4:
                    case 5: 
                        if (InputState == true)
                            _inputTestState = -1;
                        else
                            InputTestPassed = true;
                        break;
                }
                _inputTestState++;
                _inputTestState %= 6;
            }
        }

        const float  _adc24vFactor = 30.45f/5.0f;
        const int _24vAcqChannel = 1;
        const int _24vValidTargetCount = 5;
        const float _24vTestTolerance = 0.2f;
        const float _24vTestLowLimit = 18.0f;
        const float _24vTestMaxLimit = 24.0f;

        private float Average(float inp, float prev, int n)
        {
            float result = (inp + prev*(n-1)) / n;
            int r = (int)((result * 100) + 0.5f);
            result = ((float)r) / 100.0f;
            return result;
        }

        private void Test24vOut()
        {
            if (_acqDevice.IsConnected())
            {
                float prev = Measured24V;
                Measured24V = _acqDevice.ReadAnalogIn(_24vAcqChannel) * _adc24vFactor;
                
                if( Math.Abs(prev - Measured24V) > 5.0f )
                    prev = Measured24V;
                Measured24V = Average(Measured24V, prev, 5);

                if ((_24vTestLowLimit <= Measured24V) && (Measured24V <= _24vTestMaxLimit)
                    && Math.Abs(Measured24V - prev) <= _24vTestTolerance)
                    _24vValidCount++;
                else
                    _24vValidCount = 0;

                if (_24vValidCount >= _24vValidTargetCount)
                    Out24vTestPassed = true;

               // _24vPreviousVoltage = Measured24V;
            }
        }

        const float _adc10vFactor = 2.753f/0.915f; //15.0f / 5.0f;
        const float _10vTestVoltTolerance = 0.5f;
        const float _10vTestVoltStep = 2.5f;
        const float _10vTestLastVoltage = 10.0f;
        const int _10vAcqChannel = 2;

        private void TestDim()
        {
            MeasuredDim = _acqDevice.ReadAnalogIn(_10vAcqChannel) * _adc10vFactor;
            int roundDim = (int)((MeasuredDim + 0.005f)*100);
            MeasuredDim = roundDim;
            MeasuredDim /= 100;

            if (LatchActiveAr[0] && DimTestPassed == false && (_dimTestVoltage <= _10vTestLastVoltage) && _acqDevice.IsConnected())
            {
                //float out10V = _acqDevice.ReadAnalogIn(_10vAcqChannel) * _adc10vFactor;

                if ((MeasuredDim > (_dimTestVoltage - _10vTestVoltTolerance)) && (MeasuredDim < (_dimTestVoltage + _10vTestVoltTolerance)))
                {
                    _dimTestVoltage += _10vTestVoltStep;
                    if (_dimTestVoltage <= _10vTestLastVoltage)
                        Dim = (int)(_dimTestVoltage * 100);
                    else
                    {
                        DimTestPassed = true;
                        Dim = 0;
                    }
                }
                else
                {
                    _dimTestVoltage = 0;
                    Dim = 0;
                }
            }
            else //if (DimTestPassed == false)
            {
                Dim = 0;
                _dimTestVoltage = 0;
            }
        }

        private void TestBitrate()
        {
            if (Modem.RxBitrate >= 10)
                RxTestPassed = true;
            else
                RxTestPassed = false;

            if (Modem.TxBitrate >= 10)
                TxTestPassed = true;
            else
                TxTestPassed = false;
        }

        void TestSwitchStatus()
        {
            List<int> relayNums = new List<int>();

            relayNums.Add(0);

            if (Relay2Installed)
                relayNums.Add(1);
            if (Relay34Installed)
            {
                relayNums.Add(2);
                relayNums.Add(3);
            }

            foreach(int i in relayNums)
            {
                if (SwitchStatusAr[i] == "on" || SwitchStatusAr[i] == "auto")
                    _switchStatusBits[i] |= 1;
                else if( SwitchStatusAr[i] == "off")
                    _switchStatusBits[i] |= 2;
                else if (SwitchStatusAr[i] == "override" || SwitchStatusAr[i] == "ovr")
                    _switchStatusBits[i] |= 4;

                if (_switchStatusBits[i] == 7 && ( SwitchStatusAr[i] == "on" || SwitchStatusAr[i] == "auto"))
                    SwitchStatusTestPassed[i] = true;
                else
                    SwitchStatusTestPassed[i] = false;
            }
        }
        
        const byte _Relay1AcqChannel = 5;
        const byte _Relay2AcqChannel = 6;
        const byte _Relay3AcqChannel = 7;
        const byte _Relay4AcqChannel = 8;
        const byte _McsAcqChannel = 3;

        int[] _relayTestState;
        bool[] _relayPrevState;
        byte[] relayAcqChannels = new byte[]{_Relay1AcqChannel,_Relay2AcqChannel,_Relay3AcqChannel,_Relay4AcqChannel};

        void TestRelayWithMCS()
        {
            byte acqChannel = relayAcqChannels[0];
            bool relayState = _acqDevice.ReadDigIn(acqChannel) ^ true;
            RelaysState[0] = relayState;

            if (SwitchStatusTestPassed[0] == true && ( SwitchStatusAr[0] == "on" || SwitchStatusAr[0] == "auto") && RelayTestPassed[0] == false)
            {
                switch (_relayTestState[0])
                {
                    case 0:
                    case 2:
                        // push MCS button
                        _acqDevice.WriteDigOut(_McsAcqChannel, true);
                        Thread.Sleep(200);
                        _acqDevice.WriteDigOut(_McsAcqChannel, false);
                        _relayTestState[0]++;
                        break;

                    case 1:
                    case 3:
                        if (_relayPrevState[0] != relayState)
                            _relayTestState[0]++;
                        else
                            _relayTestState[0] = 0;
                        break;

                    case 4:
                        RelayTestPassed[0] = true;
                        break;
                }
                _relayPrevState[0] = relayState;
            }
        }

        void TestRelay(int num)
        {
            num--;
            if (SwitchStatusTestPassed[num] == true && (SwitchStatusAr[num] == "on" || SwitchStatusAr[num] == "auto") && RelayTestPassed[num] == false)
            {
                byte acqChannel = relayAcqChannels[num];
                bool relayState = _acqDevice.ReadDigIn(acqChannel) ^ true;
                RelayTestPassed[num] = false;
                int i = num+1;
                DoYfmCommand("/obix/yfm/relay" + i.ToString() + "/latchActive/", "false");
                Thread.Sleep(200);
                relayState = _acqDevice.ReadDigIn(acqChannel) ^ true;
                if (relayState == false)
                {
                    DoYfmCommand("/obix/yfm/relay" + i.ToString() + "/latchActive/", "true");
                    Thread.Sleep(200);
                    relayState = _acqDevice.ReadDigIn(acqChannel) ^ true;
                    if (relayState == true)
                        RelayTestPassed[num]=true;
                }
            }
        }

        private bool DoYfmCommand(string uri, string val)
        {
            try
            {
                ObixEntry[] obixEntries = new ObixEntry[] { new ObixEntry(uri, val) };
                Write(uri,val);
            }
            catch (Exception)
            {
                return false;
            }
            return true;
        }
        #endregion Hardware Test functions
    }
}
